diff options
Diffstat (limited to 'app/[lng]/test')
| -rw-r--r-- | app/[lng]/test/agentforce-poc-faq/page.tsx | 62 | ||||
| -rw-r--r-- | app/[lng]/test/agentforce-poc-summary/page.tsx | 291 |
2 files changed, 353 insertions, 0 deletions
diff --git a/app/[lng]/test/agentforce-poc-faq/page.tsx b/app/[lng]/test/agentforce-poc-faq/page.tsx new file mode 100644 index 00000000..493828d6 --- /dev/null +++ b/app/[lng]/test/agentforce-poc-faq/page.tsx @@ -0,0 +1,62 @@ +'use client' + +import { useState, useRef } from 'react' + +export default function AgentforcePocPage() { + const [isLoading, setIsLoading] = useState(true) + const iframeRef = useRef<HTMLIFrameElement>(null) + + const handleIframeLoad = () => { + setIsLoading(false) + } + + const refreshIframe = () => { + if (iframeRef.current) { + setIsLoading(true) + iframeRef.current.src = iframeRef.current.src + } + } + + return ( + <div className="min-h-screen bg-gray-50 p-4"> + <div className="max-w-7xl mx-auto"> + <div className="mb-6 flex items-center justify-between"> + <h1 className="text-3xl font-bold text-gray-900"> + Agentforce POC + </h1> + <button + onClick={refreshIframe} + className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors" + > + 새로고침 + </button> + </div> + + <div className="bg-white rounded-lg shadow-lg overflow-hidden"> + {isLoading && ( + <div className="absolute inset-0 bg-white bg-opacity-75 flex items-center justify-center z-10"> + <div className="text-center"> + <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div> + <p className="text-gray-600">로딩 중...</p> + </div> + </div> + )} + + <div className="relative" style={{ height: '800px' }}> + <iframe + ref={iframeRef} + src="/agentforce-poc.html" + title="Agentforce POC" + className="w-full h-full border-0" + onLoad={handleIframeLoad} + /> + </div> + </div> + + <div className="mt-4 text-sm text-gray-500 text-center"> + 이 페이지는 Salesforce Agentforce POC를 iframe으로 표시합니다. + </div> + </div> + </div> + ) +}
\ No newline at end of file diff --git a/app/[lng]/test/agentforce-poc-summary/page.tsx b/app/[lng]/test/agentforce-poc-summary/page.tsx new file mode 100644 index 00000000..fc504cd3 --- /dev/null +++ b/app/[lng]/test/agentforce-poc-summary/page.tsx @@ -0,0 +1,291 @@ +"use client"; + +import * as React from "react"; +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Plus, Trash2, Send, Loader2 } from "lucide-react"; +import { toast } from "sonner"; + +interface ChatMessage { + id: string; + sender: "SHI" | "Partner"; + message: string; +} + +interface SummaryResponse { + issue: string; + request: string; + summary: string; +} + +export default function AgentForcePOCSummaryPage() { + const [messages, setMessages] = useState<ChatMessage[]>([ + { + id: "1", + sender: "SHI", + message: "최근 입고된 발전기 제어반 견적서에서 납기 조건 누락을 확인했습니다. 계약 전 꼭 납기 포함한 수정본 제출 부탁드립니다." + }, + { + id: "2", + sender: "Partner", + message: "네, 누락 사항 확인 후 납기 조건 명시하여 오늘 중으로 재제출 하겠습니다." + }, + { + id: "3", + sender: "SHI", + message: "감사합니다. 납기 조건은 계약 핵심 조항입니다. 반드시 반영해 주세요." + } + ]); + + const [summaryResult, setSummaryResult] = useState<SummaryResponse | null>(null); + const [isLoading, setIsLoading] = useState(false); + + // 새 메시지 추가 + const addMessage = () => { + const newMessage: ChatMessage = { + id: Date.now().toString(), + sender: "SHI", + message: "" + }; + setMessages([...messages, newMessage]); + }; + + // 메시지 삭제 + const removeMessage = (id: string) => { + if (messages.length > 1) { + setMessages(messages.filter(msg => msg.id !== id)); + } else { + toast.error("최소 1개의 메시지는 유지해야 합니다."); + } + }; + + // 메시지 업데이트 + const updateMessage = (id: string, field: keyof ChatMessage, value: string) => { + setMessages(messages.map(msg => + msg.id === id ? { ...msg, [field]: value } : msg + )); + }; + + // 요약 API 호출 + const handleSummarize = async () => { + // 유효성 검사 + const emptyMessages = messages.filter(msg => !msg.message.trim()); + if (emptyMessages.length > 0) { + toast.error("모든 메시지를 입력해주세요."); + return; + } + + setIsLoading(true); + try { + // 토큰 가져오기 + const tokenResponse = await fetch('https://pyheroku-d21d18e4f257.herokuapp.com/api/getToken', { + method: 'POST' + }); + + if (!tokenResponse.ok) { + throw new Error('토큰을 가져오는데 실패했습니다.'); + } + + const tokenData = await tokenResponse.json(); + const accessToken = tokenData.access_token; + + if (!accessToken) { + throw new Error('토큰이 없습니다.'); + } + + // 요약 API 호출 + const summaryResponse = await fetch(`https://connect-flow-8014--ps.sandbox.my.salesforce.com/services/apexrest/summarizeChat/`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${accessToken}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + chatlog: JSON.stringify(messages.map(msg => ({ + sender: msg.sender, + message: msg.message + }))) + }) + }); + + if (!summaryResponse.ok) { + throw new Error(`API 호출 실패: ${summaryResponse.status}`); + } + + const result = await summaryResponse.json(); + setSummaryResult(result); + toast.success("요약이 완료되었습니다."); + } catch (error) { + console.error('요약 API 호출 실패:', error); + toast.error(`요약 중 오류가 발생했습니다: ${error instanceof Error ? error.message : '알 수 없는 오류'}`); + } finally { + setIsLoading(false); + } + }; + + return ( + <div className="container mx-auto py-8 space-y-6"> + <div className="text-center space-y-2"> + <h1 className="text-3xl font-bold">AgentForce POC 요약 테스트</h1> + <p className="text-muted-foreground"> + 대화 기록을 입력하고 AI 요약 기능을 테스트해보세요 + </p> + </div> + + <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> + {/* 입력 섹션 */} + <Card> + <CardHeader> + <CardTitle className="flex items-center justify-between"> + 대화 기록 입력 + <Button onClick={addMessage} size="sm" variant="outline"> + <Plus className="h-4 w-4 mr-2" /> + 메시지 추가 + </Button> + </CardTitle> + </CardHeader> + <CardContent className="space-y-4"> + {messages.map((message, index) => ( + <div key={message.id} className="space-y-2 p-4 border rounded-lg"> + <div className="flex items-center justify-between"> + <div className="flex items-center gap-2"> + <Badge variant={message.sender === "SHI" ? "default" : "secondary"}> + {message.sender} + </Badge> + <span className="text-sm text-muted-foreground">메시지 {index + 1}</span> + </div> + <Button + onClick={() => removeMessage(message.id)} + size="sm" + variant="ghost" + className="text-red-500 hover:text-red-700" + > + <Trash2 className="h-4 w-4" /> + </Button> + </div> + + <div className="space-y-2"> + <div className="flex gap-2"> + <Button + size="sm" + variant={message.sender === "SHI" ? "default" : "outline"} + onClick={() => updateMessage(message.id, "sender", "SHI")} + > + SHI + </Button> + <Button + size="sm" + variant={message.sender === "Partner" ? "default" : "outline"} + onClick={() => updateMessage(message.id, "sender", "Partner")} + > + Partner + </Button> + </div> + + <Textarea + value={message.message} + onChange={(e) => updateMessage(message.id, "message", e.target.value)} + placeholder="메시지를 입력하세요..." + className="min-h-20" + /> + </div> + </div> + ))} + + <Button + onClick={handleSummarize} + disabled={isLoading} + className="w-full" + > + {isLoading ? ( + <> + <Loader2 className="h-4 w-4 mr-2 animate-spin" /> + 요약 중... + </> + ) : ( + <> + <Send className="h-4 w-4 mr-2" /> + 요약하기 + </> + )} + </Button> + </CardContent> + </Card> + + {/* 결과 섹션 */} + <Card> + <CardHeader> + <CardTitle>요약 결과</CardTitle> + </CardHeader> + <CardContent className="space-y-4"> + {summaryResult ? ( + <div className="space-y-4"> + <div className="space-y-2"> + <h4 className="font-semibold text-sm text-muted-foreground">이슈</h4> + <div className="p-3 bg-muted rounded-md"> + {summaryResult.issue} + </div> + </div> + + <div className="space-y-2"> + <h4 className="font-semibold text-sm text-muted-foreground">요청</h4> + <div className="p-3 bg-muted rounded-md"> + {summaryResult.request} + </div> + </div> + + <div className="space-y-2"> + <h4 className="font-semibold text-sm text-muted-foreground">요약 메시지</h4> + <div className="p-3 bg-muted rounded-md"> + {summaryResult.summary} + </div> + </div> + </div> + ) : ( + <div className="text-center py-8 text-muted-foreground"> + <Send className="h-12 w-12 mx-auto mb-4 opacity-50" /> + <p>요약하기 버튼을 클릭하여 결과를 확인하세요</p> + </div> + )} + </CardContent> + </Card> + </div> + + {/* 미리보기 섹션 */} + <Card> + <CardHeader> + <CardTitle>API 요청 미리보기</CardTitle> + </CardHeader> + <CardContent> + <div className="space-y-4"> + <div> + <h4 className="font-semibold text-sm text-muted-foreground mb-2">Request Body</h4> + <pre className="bg-muted p-4 rounded-md text-sm overflow-x-auto"> + {JSON.stringify({ + conversation: JSON.stringify(messages.map(msg => ({ + sender: msg.sender, + message: msg.message + }))) + }, null, 2)} + </pre> + </div> + + {/* <div> + <h4 className="font-semibold text-sm text-muted-foreground mb-2">Headers</h4> + <pre className="bg-muted p-4 rounded-md text-sm overflow-x-auto"> + {JSON.stringify({ + 'Authorization': 'Bearer {access_token}', + 'Content-Type': 'application/json' + }, null, 2)} + </pre> + </div> */} + </div> + </CardContent> + </Card> + </div> + ); +} |
